﻿using ServiceStack.Common.Web;
using ServiceStack.Configuration;
using ServiceStack.ServiceHost;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
using System;
using System.Web;

namespace OneLoginPlugin
{
    /// <summary>
    /// SAMLAuthProvider is capable of being plugged into the ServiceAPI authentication pipeline to provide an 
    /// alternative authentication mechanism.  
    /// </summary>
    public class SAMLAuthProvider : AuthProvider
    {
        public const string Name = "SAML";
        public const string SAML_REQUEST = "SAMLRequest";
        public const string SAML_RESPONSE = "SAMLResponse";
        public const string RELAY_STATE = "RelayState";

        public static string Realm = "/auth/SAML";

        private AccountSettings _accountSettings;

        public SAMLAuthProvider(IResourceManager appSettings)
            : base(appSettings, Realm, Name)
        {
            _accountSettings = new AccountSettings(appSettings);
        }

        
        
        public override object Authenticate(ServiceStack.ServiceInterface.IServiceBase authService, IAuthSession session, Auth request)
        {

            var httpReq = authService.RequestContext != null ? authService.RequestContext.Get<IHttpRequest>() : null;
            
            Response samlResponse = new Response(_accountSettings);
            samlResponse.LoadXmlFromBase64(httpReq.FormData[SAML_RESPONSE]);

            string relayState = httpReq.FormData[RELAY_STATE];

            if (samlResponse.IsValid())
            {
                session.IsAuthenticated = true;
                session.UserAuthName = samlResponse.GetNameID();

                OnAuthenticated(authService, session, null, null);

                authService.SaveSession(session);

                return authService.Redirect( HttpUtility.UrlDecode(relayState));
            }

            throw HttpError.Unauthorized("Unable to authenticate.");
        }

        public override bool IsAuthorized(IAuthSession session, IOAuthTokens tokens, Auth request = null)
        {
            if (request != null)
            {
                if (!LoginMatchesSession(session, request.UserName)) return false;
            }

            return !String.IsNullOrEmpty(session.UserAuthName);

        }
    }
}
